import bisect

import wx
import wx.lib.scrolledpanel as scrolled
from wx.lib.pubsub import Publisher as pub
import wx.grid as gridlib

from genutils.get_logger import get_logger
from .wxPlotPanel import PlotPanel
from .utils import image_from_file

__all__ = ['Vertical_image_panel', 
           'Tree_panel',
           'String_info_panel',
           'Expe_plot_panel',
           'Data_viewer_panel',
           'Browse_panel',
           'Cart_panel',
           'Search_panel']

class Vertical_image_panel(wx.Panel):
    'This panel displays images in a scrolling panel vertically.'
    def __init__(self, parent, id, title, **kwargs):
        wx.Panel.__init__(self, parent, id, **kwargs)
        self.parent = parent
        self.logger, self.logger_name = get_logger(kwargs,
                                               'View_data.Vertical_image_panel')
        description = wx.StaticText(self, wx.ID_ANY, title)
        self.images = []

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(description, proportion=0, 
                  flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.ALL,
                  border = 7)
        self.SetSizer(sizer)

    def add_image(self, image):
        '''Add an image to the bottom.'''
        view  = wx.StaticBitmap(self, wx.ID_ANY, image, 
                                size=(image.GetWidth(),image.GetHeight()),
                                style=wx.BORDER_SIMPLE)
        self.images.append(view)
        self.GetSizer().Add(self.images[-1], proportion=0, 
                            flag=wx.BOTTOM|wx.LEFT|wx.RIGHT,
                            border=7)
        self.Layout()

    def clear_images(self):
        '''Clear all images from this panel.'''
        for i in xrange(len(self.images)):
            self.GetSizer().Remove(self.images[i])
            self.images[i].Destroy()

        self.images = []


class Tree_panel(wx.Panel):
    """
    This panel displays the contents of an electrophysiological
        database in a tree and handles updating the other panels
        due to what part of the tree is currently selected.
    """
    def __init__(self, parent, id, **kwargs ):
        self.parent = parent
        self.logger, self.logger_name = get_logger(kwargs,
                                                'View_data.notebook.Tree_panel')
        wx.Panel.__init__(self, parent, id, **kwargs)

        self.tree = wx.TreeCtrl(self, wx.ID_ANY, wx.DefaultPosition,
                               style=wx.TR_DEFAULT_STYLE)

        self.root = self.tree.AddRoot("Databases")
        self.db_root = []
        self.tree.SetPyData(self.root, None)

        self.tree.Expand(self.root)
        self.Bind(wx.EVT_TREE_SEL_CHANGED, self._OnSelChanged, self.tree)
        #self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self._OnItemExpanded, self.tree)
        #self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self._OnItemCollapsed, self.tree)
        #self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self._OnBeginEdit, self.tree)
        #self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self._OnEndEdit, self.tree)
        #self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self._OnActivate, self.tree)

        self.tree.Bind(wx.EVT_LEFT_DOWN, self._OnLeftClick)
        self.tree.Bind(wx.EVT_RIGHT_DOWN, self._OnRightClick)
        #self.tree.Bind(wx.EVT_RIGHT_DOWN, self._OnRightDown)
        #self.tree.Bind(wx.EVT_RIGHT_UP, self._OnRightUp)

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.tree, 1, flag=wx.EXPAND)
        self.SetSizer(self.sizer)
        self._last_item_selected = self.root
        self.tree_leaves = {}

        # subscribe to events
        pub.subscribe(self._build_tree, "BUILD TREE")
        pub.subscribe(self._toggle_in_cart_text, "ITEM ADDED TO CART")
        pub.subscribe(self._toggle_in_cart_text, "ITEM REMOVED FROM CART")

    def _build_tree(self, message):
        experiment_tree = message.data
        all_experiment_tree_items = {}

        tree_root = self.tree.AppendItem(self.root, "Database" )

        spec_ids = experiment_tree.keys()
        spec_ids.sort()

        # build the rest of the tree
        for this_spec_id in spec_ids:
            sess_ids = experiment_tree[this_spec_id].keys()
            sess_ids.sort()
            
            this_spec_exp = experiment_tree[this_spec_id][sess_ids[0]][0]

            spec_date = this_spec_exp.date_as_string
            # add specimin to tree
            this_spec = self.tree.AppendItem(tree_root, "Specimin[%d](%s)"
                                             % (this_spec_id, spec_date))
            self.tree.SetPyData(this_spec, this_spec_exp)


            for this_sess_id in sess_ids:
                experiments = experiment_tree[this_spec_id][this_sess_id]
                this_sess_exp = experiment_tree[this_spec_id][this_sess_id][0]
                # add session to tree
                this_sess = self.tree.AppendItem(this_spec, "Session[%d](%d)"
                                                 % (this_sess_id,
                                                    len(experiments)))
                self.tree.SetPyData(this_sess, this_sess_exp)

                for experiment in experiments:
                    this_expe = self.tree.AppendItem(this_sess, 
                                                "Experiment[%d]%s(%s)" % 
                                                (experiment.expe_id,
                                                experiment.stim_type,
                                                experiment.expe_time_as_string))
                    all_experiment_tree_items[experiment.expe_id] = this_expe
                    self.tree.SetPyData(this_expe, experiment)
        # put the name of the database at the root
        dbid = experiment.dbid
        root_item = self.tree.SetItemText(tree_root, "Database [%s]" % dbid)

        self.tree_leaves[dbid] = all_experiment_tree_items

        self.tree.Expand(self.root)
        self.tree.Expand(tree_root)

    def _toggle_in_cart_text(self, message):
        experiment = message.data
        dbid = experiment.dbid
        expe_id = experiment.expe_id
        item = self.tree_leaves[dbid][expe_id]
        this_item_text = self.tree.GetItemText(item)
        # if its not there add it.
        if '(in cart)' in this_item_text:
            self.tree.SetItemText(item, 
                                  this_item_text[:-10])
        else:
            self.tree.SetItemText(item, this_item_text + " (in cart)")

    def _OnLeftClick(self, event):
        # send this item off to OnSelChanged
        pt = event.GetPosition()
        item, flags = self.tree.HitTest(pt)
        self.logger.debug("Left-clicked tree item '%s'" % 
                          self.tree.GetItemText(item))
        self._OnSelChanged(event, item=item)
        event.Skip()

    def _OnRightClick(self, event):
        # are we clicking on an experiment?
        pt = event.GetPosition()
        item, flags = self.tree.HitTest(pt)

        this_item_text = self.tree.GetItemText(item)
        experiment = self.tree.GetPyData(item)
        self.logger.debug("Right-clicked on a tree item '%s'   %s" % 
                          (experiment, self.tree.GetItemText(item)))
        if "Experiment" in this_item_text:
            pub.sendMessage("RIGHT-CLICKED EXPERIMENT IN TREE", experiment)

    def _OnSelChanged(self, event, item=None):
        if item is None:
            item = event.GetItem()
        if item == self._last_item_selected:
            return
        self._last_item_selected = item
 
        this_item_text = self.tree.GetItemText(item)
        experiment     = self.tree.GetPyData(item)
        self.logger.debug("Selection changed to tree item: %s" % experiment)
        if "Experiment" in this_item_text:
            pub.sendMessage("SETUP EXPERIMENT", experiment)

        if "Session" in this_item_text:
            pub.sendMessage("SETUP SESSION", experiment)

        if "Specimin" in this_item_text:
            pub.sendMessage("SETUP SPECIMIN", experiment)
        event.Skip()


class String_info_panel(wx.Panel):
    '''
    This is the string info panel, it displays information about 
        the a single topic.
    '''
    def __init__(self, parent, id, topic, **kwargs):
        wx.Panel.__init__(self, parent, id, **kwargs)
        self.parent = parent
        self.topic = topic
        self.logger, self.logger_name = get_logger(kwargs,
                                                'View_data.string_info_panel')
        self.cp = wx.CollapsiblePane(self, wx.ID_ANY, label=topic)
        self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnCollapse, self.cp)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.cp, 0, wx.EXPAND|wx.ALL)
        self.SetSizer(sizer)

    def set_topic(self, topic):
        self.cp.SetLabel(topic)

    def OnCollapse(self, evt=None):
        self.logger.debug("clicked on collapsible pane '%s'" %
                          self.topic)
        self.parent.Layout()
        if evt:
            evt.Skip()

    def setup_ui(self):
        info_string = wx.StaticText(self.cp.GetPane(), wx.ID_ANY, "")
        font = wx.Font(10, wx.DEFAULT, wx.NORMAL, 
                       wx.NORMAL, False, 'Courier 10 Pitch' )
        info_string.SetFont(font)

        cpsizer = wx.BoxSizer(wx.VERTICAL)
        cpsizer.Add(info_string, 0, wx.EXPAND|wx.ALL, border=7)
        self.cp.SetSizer(cpsizer)

        self.info_string = info_string
        self.everything = [info_string]

        # disable everything initially
        self.disable_everything()

    def disable_everything(self):
        """
        Disable all fields and buttons for this entire panel.
        """
        for thing in self.everything:
            thing.Disable()
        self.Disable()

    def enable_everything(self):
        for thing in self.everything:
            thing.Enable()
        self.Enable()

    def set_info(self, info_string):
        self.info_string.SetLabel(info_string)


class Expe_plot_panel(PlotPanel):
    def __init__( self, parent, id, **kwargs ):
        self.parent = parent
        self.logger, self.logger_name = get_logger(kwargs,
                                                   'View_data.expe_plot_panel')

        PlotPanel.__init__( self, parent, id, **kwargs )
        self.SetColor( (255,255,255) )

    def set_data_from_database(self, db_path, sess_id, expe_id):
        # figure out what file we want to open and collect data from
        file_path = os.path.join(db_path[:-15], "session_%d" % sess_id)
        file_path = os.path.join(file_path, "experiment_%d" % expe_id)
        # identify the data file in this directory
        files = os.listdir(file_path)
        for file in files:
            if str(expe_id) == file[:len(str(expe_id))] and 'times.cPickle' not in file:
                data_file = file
        filename = os.path.join(file_path, data_file)
        raw_data = numpy.array(raw_data_columns(filename))
        if raw_data.shape[0] == 0:
            # make the title of the plot reflect that this data file is empty
            return
        else:
            # FIXME assumes that times are column 4 and voltages are column 1
            self.set_data(raw_data.T[3], raw_data.T[0])

    def set_data(self, x_values, y_values):
        self.logger.debug('set values.')
        self.x_values = x_values
        self.y_values = y_values
        self.figure.clf()
        self.subplot = self.figure.add_subplot(111 ,ylabel='mV', xlabel='ms')


    def draw( self ):
        """Draw data."""
        if not hasattr( self, 'subplot' ):
            self.logger.debug('Setting up axes')
            self.subplot = self.figure.add_subplot(111, title='Experimental Data' ,ylabel='mV', xlabel='ms')

        if hasattr(self, 'x_values') and hasattr(self, 'y_values'):
            self.logger.debug('Plotting data.')
            self.subplot.plot( self.x_values, self.y_values )
        else:
            self.logger.info("Nothing to plot, please call set_data().")


class Data_viewer_panel(wx.Panel):
    '''
    This panel organizes the panels used to view details about the data 
        in the database.
    '''
    def __init__(self, parent, id, **kwargs):
        wx.Panel.__init__(self, parent, id, **kwargs)
        self.parent = parent
        self.logger, self.logger_name = get_logger(kwargs,
                                                   'View_data.data_viewer')

        # instantiate the spec_info_panel
        self.sp = scrolled.ScrolledPanel(self, wx.ID_ANY, 
                                         style=wx.BORDER_SUNKEN)
        self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self._OnCollapse)
        self.spec_panel  = String_info_panel(self.sp, wx.ID_ANY, 
                            'Specimin                                      ')
        self.sess_panel  = String_info_panel(self.sp, wx.ID_ANY, 
                            'Session                                       ')
        self.expe_panel  = String_info_panel(self.sp, wx.ID_ANY, 
                            'Experiment                                    ')
        self.spec_panel.setup_ui()
        self.sess_panel.setup_ui()
        self.expe_panel.setup_ui()

        # add elements to the scrolled panel sizer
        sp_sizer = wx.BoxSizer(wx.VERTICAL)
        sp_sizer.Add(self.spec_panel, proportion=0, border=3)
        sp_sizer.Add(self.sess_panel, proportion=0, border=3)
        sp_sizer.Add(self.expe_panel, proportion=0, border=3)
        self.sp.SetSizer(sp_sizer)
        self.sp.SetupScrolling()

        self.image_panel = Vertical_image_panel(self, wx.ID_ANY, 
                                                "Eye Cup/RF Images")
        """
        # plot_data checkbox
        self.plot_check_box = wx.CheckBox(self, wx.ID_ANY, label="Plot Data")
        self.Bind(wx.EVT_CHECKBOX, self.OnPlot, self.plot_check_box)

        # set up the window to plot experimental data
        self.expe_plot_panel = Expe_plot_panel(self, wx.ID_ANY)
        self.expe_plot_panel.size = (1080,354)
        """

        # set up the sizer for this panel
        self.v_sizer = wx.BoxSizer(wx.VERTICAL)
        self.top_h_sizer = wx.BoxSizer(wx.HORIZONTAL)
        
        self.v_sizer.Add(self.top_h_sizer, proportion=1, flag=wx.EXPAND)
        #sizer1.Add(plotter, proportion=1, flag=wx.EXPAND)

        self.top_h_sizer.Add(self.sp, proportion=1, flag=wx.EXPAND)

        AL = wx.ALIGN_LEFT
        AR = wx.ALIGN_RIGHT
        self.top_h_sizer.Add(self.image_panel, proportion=0, flag=AR)
        self.SetSizer(self.v_sizer)
        self.top_h_sizer.Layout()
        self.Layout()
            
        pub.subscribe(self._setup_specimin,   "SETUP SPECIMIN")
        pub.subscribe(self._setup_session,    "SETUP SESSION")
        pub.subscribe(self._setup_experiment, "SETUP EXPERIMENT")

    def _OnCollapse(self, evt=None):
        self.sp.SetupScrolling()
        if evt:
            evt.Skip()

    def _setup_specimin(self, message):
        experiment = message.data
        self.spec_panel.enable_everything()
        self.sess_panel.disable_everything()
        self.expe_panel.disable_everything()

        self._update_specimin(experiment)
        self.image_panel.clear_images()
        self.sp.SetupScrolling()
        self.Layout()

    def _setup_session(self, message):
        experiment = message.data
        self.spec_panel.enable_everything()
        self.sess_panel.enable_everything()
        self.expe_panel.disable_everything()

        self._update_specimin(experiment)
        self._update_session(experiment)
        self.image_panel.clear_images()
        self.image_panel.add_image(image_from_file(
                                   experiment.eye_cup_image_path))
        self.sp.SetupScrolling()
        self.Layout()

    def _setup_experiment(self, message):
        experiment = message.data
        self.spec_panel.enable_everything()
        self.sess_panel.enable_everything()
        self.expe_panel.enable_everything()

        self._update_specimin(experiment)
        self._update_session(experiment)
        self._update_experiment(experiment)
        self.image_panel.clear_images()
        self.image_panel.add_image(image_from_file(
                                   experiment.eye_cup_image_path))
        self.image_panel.add_image(image_from_file(
                                   experiment.rf_image_path))
        self.sp.SetupScrolling()
        self.Layout()

    def _update_specimin(self, experiment):
        self.spec_panel.set_info(experiment.spec_info_string)
        self.spec_panel.set_topic("Specimin #%d from '%s'" %
                                (experiment.spec_id, experiment.dbid))

    def _update_session(self, experiment):
        self.sess_panel.set_info(experiment.sess_info_string)
        self.sess_panel.set_topic("Session #%d" % experiment.sess_id)

    def _update_experiment(self, experiment):
        self.expe_panel.set_info(experiment.expe_info_string)
        self.expe_panel.set_topic("Experiment #%d" % experiment.expe_id)

    """
    def OnPlot(self, event):
        if self.plot_check_box.IsChecked():
            tree = self.tree_panel.tree
            # find out if the tree has an experiment currently selected
            item = tree.GetSelection()
            item_text = tree.GetItemText(item)
            item_data = tree.GetPyData(item)
            if "Experiment" in item_text:
                # find out what database this is from...
                # experiment should be under session, and specimin so
                this_sess_item = tree.GetItemParent(item)
                this_spec_item = tree.GetItemParent(this_sess_item)
                this_db_item   = tree.GetItemParent(this_spec_item)
                this_db_id = tree.GetPyData(this_db_item)
                this_spec_id = tree.GetPyData(this_spec_item)
                this_sess_id = tree.GetPyData(this_sess_item)
                this_expe_id = item_data
                for i in xrange(len(self.db)):
                    if self.db[i]["id"] == this_db_id:
                        this_db_index = i
                self.expe_plot_panel.set_data_from_database(self.db[this_db_index]['path'], this_sess_id, this_expe_id )
                self.expe_plot_panel.draw()
    """


class Browse_panel(wx.Panel):
    '''
    This panel organizes the panels used to browse about the data 
        in the database.
    '''
    def __init__(self, parent, id,  **kwargs):
        wx.Panel.__init__(self, parent, id, **kwargs)
        self.parent = parent
        self.logger, self.logger_name = get_logger(kwargs,'View_data.browse')

        # open database button
        odb_button = wx.Button(self, wx.ID_ANY, "Open Database...")
        self.Bind(wx.EVT_BUTTON, self.OnOdb, odb_button)

        # instructions text
        self.results_text = wx.StaticText(self, wx.ID_ANY,
                        '(right click to add/remove experiments from cart)')

        # instantiate the tree viewer
        self.tree_panel = Tree_panel(self, wx.ID_ANY)

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(odb_button,         0, flag=wx.ALIGN_RIGHT|wx.ALL, 
                                              border=7)
        self.sizer.Add(self.results_text,  0, flag=wx.LEFT, 
                                              border=7)
        self.sizer.Add(self.tree_panel,    1, 
                       flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM,
                       border=7)
        self.SetSizer(self.sizer)

    def OnOdb(self, event):
        self.logger.debug("Open Database button clicked.")
        pub.sendMessage("OPEN A DATABASE")
        event.Skip()


class Cart_panel(wx.Panel):
    '''
    This panel organizes the panels used to view the shopping cart.
    '''
    def __init__(self, parent, id, **kwargs):
        # set up the logger
        self.logger, self.logger_name = get_logger(kwargs,
                                             'View_data.dp_notebook.cart_panel')

        wx.Panel.__init__(self, parent, id, **kwargs)
        self.parent = parent

        # shopping cart
        cart_text = wx.StaticText(self, wx.ID_ANY, 
                                 "Shopping Cart: (right click to remove item.)")
        # expe_grid
        self.cart_grid = ExperimentsGrid(self, name='cart_grid',
                                         has_cart_column = False,
                                          logger_name=self.logger_name)

        # export button
        export_button = wx.Button(self, wx.ID_ANY, "Export...")
        self.Bind(wx.EVT_BUTTON, self._OnExport, export_button)

        # removed items
        removed_cart_text = wx.StaticText(self, wx.ID_ANY, 
                     "Removed items: (right click to return to shopping cart.)")
        self.removed_cart_grid = ExperimentsGrid(self, 
                                                  name='removed_cart_grid', 
                                                  has_cart_column = False,
                                                  logger_name=self.logger_name)

        # panel_sizer
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(cart_text,              0,
                       flag=wx.ALIGN_LEFT|wx.TOP|wx.LEFT|wx.RIGHT,
                       border=12)
        self.sizer.Add(self.cart_grid,         1,
                       flag=wx.EXPAND|wx.LEFT|wx.RIGHT,
                       border=7)
        self.sizer.Add(export_button,          0, 
                       flag=wx.ALIGN_RIGHT|wx.ALL,
                       border=7)
        self.sizer.Add(removed_cart_text,      0,
                       flag=wx.ALIGN_LEFT|wx.TOP|wx.LEFT|wx.RIGHT,
                       border=12)
        self.sizer.Add(self.removed_cart_grid, 1,
                       flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM,
                       border=7)
        self.SetSizer(self.sizer)
        pub.subscribe(self._add_to_cart, "ITEM ADDED TO CART")
        pub.subscribe(self._remove_from_cart, "ITEM REMOVED FROM CART")

    def _OnExport(self, event):
        self.logger.debug("pressed export data button.")
        pub.sendMessage("EXPORT DATA")
        event.Skip()

    def _add_to_cart(self, message):
        experiment = message.data
        self.cart_grid.add_experiment(experiment)
        self.removed_cart_grid.remove_experiment(experiment)
        self.cart_grid.AutoSizeColumns()
        self.removed_cart_grid.AutoSizeColumns()

    def _remove_from_cart(self, message):
        experiment = message.data
        self.removed_cart_grid.add_experiment(experiment)
        self.cart_grid.remove_experiment(experiment)
        self.cart_grid.AutoSizeColumns()
        self.removed_cart_grid.AutoSizeColumns()


class Search_panel(wx.Panel):
    '''
    This panel organizes the panels used to search the database.
    '''
    def __init__(self, parent, id, **kwargs):
        # set up the logger
        self.logger, self.logger_name = get_logger(kwargs,
                                        'View_data.dp_notebook.search_panel')
        wx.Panel.__init__(self, parent, id, **kwargs)

        # database name
        self.database_name = wx.StaticText(self, wx.ID_ANY, "Database Name:")
        self.database_chooser = wx.Choice(self, wx.ID_ANY, choices=[])

        self.open_db_button = wx.Button(self, wx.ID_ANY, 
                                        "Open Another Database...")
        self.Bind(wx.EVT_BUTTON, self._OnOpenDatabase, self.open_db_button)

        self.top_box_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.top_box_sizer.Add(self.database_name, 0,
             flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT|wx.ALL, border=7)
        self.top_box_sizer.Add(self.database_chooser, 0,
             flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.ALL, border=7)
        self.top_box_sizer.Add(self.open_db_button, 0,
             flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.ALL, border=7)

        # search label
        self.search_text = wx.StaticText(self, wx.ID_ANY,
                        'Search Command:    (must "SELECT expe_id")')
        # search text_ctrl
        self.search_text_ctrl = wx.TextCtrl(self, wx.ID_ANY,
                                     style=wx.TE_MULTILINE )
        self.search_text_ctrl.SetValue('SELECT expe_id')
        self.search_text_ctrl.Bind(wx.EVT_TEXT, self._OnEditSearch)

        # run_search_bar
        #-- insert date
        self.insert_date_ctrl = wx.DatePickerCtrl(self, size=(120,-1),
                                        style = wx.DP_DROPDOWN    |
                                                wx.DP_SHOWCENTURY |
                                                wx.DP_ALLOWNONE    )
        self.insert_date_button = wx.Button(self, wx.ID_ANY, "Insert Date")
        self.Bind(wx.EVT_BUTTON, self._OnInsertDate, self.insert_date_button)
        self.date_box_sizer = wx.BoxSizer(wx.VERTICAL)
        self.date_box_sizer.Add(self.insert_date_ctrl, 0)
        self.date_box_sizer.Add(self.insert_date_button, 0)

        self.prev_search_button = wx.Button(self, wx.ID_ANY, "<", size=(30,-1))
        self.run_search_button  = wx.Button(self, wx.ID_ANY, "Run Search")
        self.next_search_button = wx.Button(self, wx.ID_ANY, ">", size=(30,-1))
        self.Bind(wx.EVT_BUTTON, self._OnPrev, self.prev_search_button)
        self.Bind(wx.EVT_BUTTON, self._OnSearch,  self.run_search_button)
        self.Bind(wx.EVT_BUTTON, self._OnNext, self.next_search_button)

        self.search_box_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.search_box_sizer.Add(self.date_box_sizer, 0,
              flag=wx.ALIGN_RIGHT|wx.ALL, border=7)
        self.search_box_sizer.Add(self.prev_search_button,
             flag=wx.ALIGN_RIGHT|wx.LEFT|wx.TOP|wx.BOTTOM, border = 7)
        self.search_box_sizer.Add(self.run_search_button,
             flag=wx.ALL, border=7)
        self.search_box_sizer.Add(self.next_search_button,
             flag=wx.ALIGN_LEFT|wx.RIGHT|wx.TOP|wx.BOTTOM, border = 7)

        # results text
        self.results_text = wx.StaticText(self, wx.ID_ANY,
            'Search results:    (right click to add/remove from cart)')
        self.expe_grid = ExperimentsGrid(self, name='search_results_grid',
                                                logger_name=self.logger_name)
        self.expe_grid.SetRowLabelSize(0)

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.top_box_sizer,          0, 
                       wx.ALIGN_CENTER_HORIZONTAL)
        self.sizer.Add(self.search_text,            0, 
                       wx.ALIGN_LEFT|wx.TOP|wx.LEFT, border=7 )
        self.sizer.Add(self.search_text_ctrl,       1,
                       wx.ALIGN_LEFT|wx.EXPAND|wx.LEFT|wx.RIGHT, border=7)
        self.sizer.Add(self.search_box_sizer,       0, 
                       wx.ALIGN_RIGHT)
        self.sizer.Add(self.results_text,           0, 
                       wx.ALIGN_LEFT|wx.TOP|wx.LEFT, border=7 )
        self.sizer.Add(self.expe_grid,              3,
                       wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM, border=7)
        self.SetSizer(self.sizer)
        self.original_search_string = ''

        pub.subscribe(self._update_database_chooser, "UPDATE DATABASE CHOOSER")
        pub.subscribe(self._search_grid_set_experiments, 
                      "SEARCH GRID SET EXPERIMENTS")
        pub.subscribe(self._search_grid_set_in_cart, "SEARCH GRID SET IN CART")

    def _update_database_chooser(self, message):
        dbid = message.data
        chooser = self.database_chooser
        choices = chooser.GetItems()
        choices.append(dbid)
        chooser.SetItems(choices)
        chooser.SetStringSelection(dbid)
        self.Layout()

    def _search_grid_set_in_cart(self, message):
        experiments = message.data
        for experiment in experiments:
            self.expe_grid.set_in_cart(experiment)

    def _search_grid_set_experiments(self, message):
        experiments = message.data
        self.expe_grid.set_experiments(experiments)
        self.expe_grid.AutoSizeColumns()

    def _OnInsertDate(self, event):
        self.logger.debug("clicked insert date button")
        # get the date from the date_ctrl and add the JDN of that date to
        # the end of what is in the search_ctrl
        search_date = self.insert_date_ctrl.GetValue().GetJDN()
        search_text = self.search_text_ctrl.GetValue()
        search_text += " %f" % (search_date)
        self.search_text_ctrl.SetValue(search_text)
        event.Skip()

    def _OnEditSearch(self, event):
        begining, end = self.search_text_ctrl.GetSelection()
        self.search_text_ctrl.SetSelection(end, end)
        self.original_search_string = ''
        event.Skip()

    def _OnSearch(self, event):
        # search the database which is selected with the string supplied in
        #  the self.search_text_ctrl
        self.logger.debug("Run Button pressed.")
        dbid = self.database_chooser.GetStringSelection()

        # perform the search
        search_string = self.search_text_ctrl.GetValue()
        pub.sendMessage("SEARCH DATABASE", (dbid, search_string))
        event.Skip()

    def get_search_string(self):
        search_string = self.search_text_ctrl.GetValue()
        if self.original_search_string == '':
            self.original_search_string = search_string
            self.search_text_ctrl.SelectAll()
        return search_string

    def _OnPrev(self, event):
        search_string = self.get_search_string()
        self.logger.debug("previous button pressed.")
        pub.sendMessage("SEARCH PANEL PREV", (search_string, 
                                              self.original_search_string))

    def _OnNext(self, event):
        search_string = self.get_search_string()
        self.logger.debug("next button pressed.")
        pub.sendMessage("SEARCH PANEL NEXT", (search_string, 
                                              self.original_search_string))

    def _OnOpenDatabase(self, event):
        self.logger.debug("clicked on open database button.")
        pub.sendMessage("OPEN A DATABASE")


class ExperimentsGrid(gridlib.Grid):
    def __init__(self, parent, name=None, has_cart_column=True, **kwargs):
        self.parent = parent
        self.name = name
        self._has_cart_column = has_cart_column
        self.logger, self.logger_name = get_logger(kwargs, 
                                                   'View_data.experiments_grid')

        gridlib.Grid.__init__(self, parent, **kwargs)
        if self._has_cart_column:
            self.CreateGrid(0,6)
        else:
            self.CreateGrid(0,5)

        # ---- column settings -----
        attr1 = gridlib.GridCellAttr()
        attr1.SetReadOnly(True)
        attr1.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER) 
        # set all columns to use this set of attributes
        self.SetColAttr(0, attr1)
        self.SetColAttr(1, attr1)
        self.SetColAttr(2, attr1)
        self.SetColAttr(3, attr1)
        self.SetColAttr(4, attr1)
        # set the column labels
        self.SetColLabelValue(0, "Date")
        self.SetColLabelValue(1, "Time")
        self.SetColLabelValue(2, "Stimulus")
        self.SetColLabelValue(3, "ID")
        self.SetColLabelValue(4, "Database")
        # set the column widths
        self.SetColSize(0, 80)
        self.SetColSize(1, 80)
        self.SetColSize(2, 150)
        self.SetColSize(3, 40)
        self.SetColSize(4, 90)
        if self._has_cart_column:
            self.SetColSize(5, 40)
            self.SetColAttr(5, attr1)
            self.SetColLabelValue(5, "Cart")

        self.Bind(gridlib.EVT_GRID_CELL_RIGHT_CLICK, self.OnRightClick)
        self.Bind(gridlib.EVT_GRID_CELL_LEFT_CLICK, self.OnLeftClick)

        # set it up so that selections select the whole row
        self.SetSelectionMode(self.wxGridSelectRows)

        self.SetRowLabelSize(0)
        # this will hold what should be in the grid
        self.expe_ids = []
        self.experiments = []

        pub.subscribe(self._added_to_cart, "ITEM ADDED TO CART")
        pub.subscribe(self._removed_from_cart, "ITEM REMOVED FROM CART")

    def OnLeftClick(self, event):
        row = event.GetRow()
        experiment = self.experiments[row]
        self.logger.debug("left-clicked on row containing %s" %
                          str(experiment))
        pub.sendMessage("SETUP EXPERIMENT", experiment)
        event.Skip()

    def OnRightClick(self, event):
        row = event.GetRow()
        experiment = self.experiments[row]
        self.logger.debug("right-clicked on row containing %s" %
                          str(experiment))
        pub.sendMessage("RIGHT-CLICKED IN GRID", experiment)

    def _added_to_cart(self, message):
        experiment = message.data
        self.set_in_cart(experiment)

    def _removed_from_cart(self, message):
        experiment = message.data
        self.set_in_cart(experiment, in_cart=False)

    def set_in_cart(self, experiment, in_cart=True):
        "Sets the 'in cart status' of the experiment given."
        try: index = self.experiments.index(experiment)
        except ValueError: return

        if not self._has_cart_column:
            return
        if in_cart:
            self.SetCellValue(index, 5, 'X')
        else:
            self.SetCellValue(index, 5, '')

    def remove_experiment(self, experiment):
        "Removes the given experiment from the grid."
        try: index = self.experiments.index(experiment)
        except ValueError: 
            return

        self.DeleteRows(index)
        del self.experiments[index]
        del self.expe_ids[index]

    def add_experiment(self, experiment, in_cart=False):
        "Adds the given experiment to the experiment grid."
        if experiment in self.experiments: return

        expe_id = experiment.expe_id
        if self.expe_ids and expe_id > self.expe_ids[-1]:
            index = len(self.expe_ids)
        else:
            index = bisect.bisect_left(self.expe_ids, expe_id)
        self.expe_ids.insert(index, expe_id)
        self.experiments.insert(index, experiment)

        data = [experiment.date_as_string, 
                experiment.expe_time_as_string,
                experiment.stim_type,
                experiment.expe_id,
                experiment.dbid,
                in_cart]

        self.InsertRows(index)
        self.SetCellValue(index, 0, str(data[0]))
        self.SetCellValue(index, 1, str(data[1]))
        self.SetCellValue(index, 2, str(data[2]))
        self.SetCellValue(index, 3, str(data[3]))
        self.SetCellValue(index, 4, str(data[4]))
        if not self._has_cart_column:
            return
        if data[5]:
            self.SetCellValue(index, 5, 'X')
        else:
            self.SetCellValue(index, 5, '')

    def set_experiments(self, experiment_list):
        "Clears all experiments, and set the experiments to the list given."
        self.clear_experiments()
        for experiment in experiment_list:
            self.add_experiment(experiment)

    def clear_experiments(self):
        "Erase all experiments from the grid."
        if len(self.experiments) != 0:
            self.DeleteRows(numRows=len(self.experiments))
            self.expe_ids = []
            self.experiments = []
